Skip to content

feat(knowledge): stage/delete knowledge pack task handlers#32

Merged
ysyneu merged 6 commits intomainfrom
feat/knowledge-pack-tasks
Apr 23, 2026
Merged

feat(knowledge): stage/delete knowledge pack task handlers#32
ysyneu merged 6 commits intomainfrom
feat/knowledge-pack-tasks

Conversation

@ysyneu
Copy link
Copy Markdown
Collaborator

@ysyneu ysyneu commented Apr 15, 2026

Summary

Adds two WebSocket task handlers so the runner can receive knowledge-pack files pushed from Safari at session start and remove them on demand. Implements the Safari-side contract in fc-safari:docs/knowledge-runner-tasks.md.

  • stage_knowledge_files — decodes each {rel_path, checksum, content_b64}, writes atomically (temp + rename), then merges entries into <workspace>/.safari-knowledge-sentinel.json under an advisory lock (flock) to handle concurrent BYOC sessions.
  • delete_knowledge_files — unlinks each rel_path (idempotent) and removes its sentinel entry under the same lock.
  • Path validation rejects /, \\, .., dotfiles (including the sentinel name itself — the runner owns the sentinel, clients don't stage it).
  • 14 subtests cover single-file stage, overwrite, mixed-validity, stage+delete cycles, missing-path idempotency, and the path-rejection matrix.

Context

Safari's EnvironmentMiddleware.BeforeModelCall lazy-stages the account's knowledge pack into the runner's workspace root so DUTY.md and @-referenced runbooks survive across sessions and ephemeral cloud pod restarts. Without these handlers the Safari end dispatches the tasks but the runner ignores them, and the agent sees an empty workspace.

Test plan

  • Unit: go test ./workspace/ -run \"TestStage|TestDelete|TestValidate\" -v — 14/14 PASS.
  • Integration with Safari: attach a rebuilt runner to a Safari session, save a file via Settings → Knowledge Packs, start a new session, confirm the file appears at workspace root.
  • Concurrent sessions on BYOC: two sessions on the same runner concurrently trigger stage — sentinel stays consistent.

🤖 Generated with Claude Code

ysyneu and others added 6 commits April 15, 2026 17:17
…k handlers

Implements the Safari-side contract documented in fc-safari's
docs/knowledge-runner-tasks.md. Atomic writes via temp+rename; sentinel
rewrites under flock to handle concurrent BYOC sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…trict '..' check

- Remove the unused `removed []string` accumulator in DeleteKnowledgeFiles;
  valid paths now go straight into `toRemove` in one loop pass, eliminating
  the second validateKnowledgeRelPath call per path.
- Tighten the '..' guard to reject only the bare ".." token — the old
  strings.Contains check wrongly rejected valid filenames like "foo..bar".
- Add test assertions confirming double-dot-in-middle filenames are accepted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… build

- .golangci.yml: remove G706 from gosec.excludes. golangci-lint v2.1.6 (pinned
  in CI) doesn't know G706 as a gosec rule, so `config verify` rejects the
  config before any linting runs. G706 wasn't enforcing anything in CI anyway.
- workspace: split the sentinel advisory-lock primitive across build-tagged
  files so `go build` / `go test` succeed on windows/amd64. Unix path keeps
  flock(2); Windows path is a no-op stub with a warning log (the runner is
  not shipped on Windows — CI only needs it to compile).

Verified: GOOS=windows go build ./... clean; unix tests PASS; golangci-lint
config verify clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- workspace: move sentinel file-open inside acquireSentinelLock. The previous
  split left withSentinelLock holding an os.File handle across fn(), which on
  Windows blocks os.Rename of the sentinel during atomicWriteFile — "Access is
  denied". Unix path still opens + flocks; Windows path is a zero-op (runner
  not shipped there).
- workspace/knowledge.go: pre-allocate `succeeded` slice at len(args.Files) —
  prealloc lint hit.
- protocol/messages.go: gofmt realign const block — TaskOpStage/Delete widened
  the longest name so all previous entries shifted right a column.

Verified: GOOS=windows go build ./... clean; go test ./workspace/ PASS;
CI-relevant golangci-lint checks (gofmt, prealloc) resolved. G706 warnings
in cmd/mcp only appear on local v2.11; CI pins v2.1 which has no G706 rule.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Safari renamed the compute-unit concept from 'workspace' / 'worknode' to
'environment'. Mirror the rename in the runner so API, protocol fields, log
keys, and docs align. No migration shim — Safari only ships the new names.

Changes:
- Go package: workspace/ → environment/ (git-mv + `package environment`).
- Type: Workspace → Environment (receivers w → e).
- Protocol: WorknodeID / worknode_id → EnvironmentID / environment_id in
  StatusPayload and WelcomeMessage (Safari's welcome emitter already uses
  these names).
- ws/client.go: field worknodeID → environmentID, log keys match.
- cmd/main.go: default URL path /safari/worknode/ws → /safari/environment/ws,
  token examples wnt_xxx → ent_xxx (Safari's BYOC auth gate accepts ent_*).
- README.md / README_zh.md: same example updates.

CLI flags (--workspace, --token) and env vars (FLASHDUTY_RUNNER_WORKSPACE)
keep their names — they describe the local workspace root on disk, which
Safari also calls WorkspaceRoot. Only the product-level concept ("the thing
Safari talks to over WS") was renamed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ysyneu ysyneu merged commit d53af54 into main Apr 23, 2026
@ysyneu ysyneu deleted the feat/knowledge-pack-tasks branch April 23, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant